package com.jkgh.asin;

import java.io.IOException;
import java.net.InetSocketAddress;
import java.nio.channels.SelectionKey;
import java.nio.channels.Selector;
import java.nio.channels.ServerSocketChannel;
import java.nio.channels.SocketChannel;
import java.util.ArrayList;
import java.util.List;

public class SelectorThread extends Thread {

	private final Selector selector;
	private final ConnectionListener listener;
	private final int port;

	public SelectorThread(int port, ConnectionListener listener) {
		this.port = port;
		this.listener = listener;
		try {
			this.selector = Selector.open();
		} catch (IOException e) {
			throw new RuntimeException(e);
		}
	}

	@Override
	public void run() {
		
		try {
			ServerSocketChannel ssc = ServerSocketChannel.open();
			ssc.configureBlocking(false);
			ssc.socket().bind(new InetSocketAddress(port));
			ssc.register(selector, ssc.validOps());
		} catch (IOException e) {
			throw new RuntimeException(e);
		}
				
		while (true) {
			
			try {
				selector.select();
			} catch (IOException e) {
				break;
			}
			
			List<SelectionKey> allKeys = new ArrayList<SelectionKey>(selector.selectedKeys());
			selector.selectedKeys().clear();
			
			for (SelectionKey sk: allKeys) {
			
				if (!sk.isValid()) {
					break;
				}
				
				if (sk.isAcceptable()) {
					ServerSocketChannel ssc = (ServerSocketChannel) sk.channel();
					try {
						SocketChannel sc = ssc.accept();
						sc.configureBlocking(false);
						SelectionKey newSK = sc.register(selector, SelectionKey.OP_READ);
						SocketChannelHandler handler = new SocketChannelHandler(newSK);
						newSK.attach(handler);
						com.jkgh.asin.State firstState = listener.connected(handler);
						handler.setCurrentState(firstState);
					} catch (IOException e) {
						sk.cancel();
						continue;
					}
				}
				
				if (!sk.isValid()) {
					break;
				}
				
				if (sk.isConnectable()) {
					SocketChannel sc = (SocketChannel) sk.channel();
					try {
						sc.finishConnect();
					} catch (IOException e) {
						sk.cancel();
						continue;
					}
				}
				
				if (!sk.isValid()) {
					break;
				}
				
				if (sk.isWritable()) {
					SocketChannelHandler ch = (SocketChannelHandler) sk.attachment();
					try {
						ch.writeNow();
					} catch (IOException e) {
						ch.disconnectOnError(listener);
					}
				}
				
				if (!sk.isValid()) {
					break;
				}
				
				if (sk.isReadable()) {
					SocketChannelHandler ch = (SocketChannelHandler) sk.attachment();
					try {
						ch.readNow();
					} catch (IOException e) {
						ch.disconnectOnError(listener);
					}
				}
			}
		}
	}

}
